package com.jiaozi.core.security.filter;


import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;

import com.jiaozi.base.redis.RedisEnum;
import com.jiaozi.base.redis.RedisUtil;
import com.jiaozi.core.exception.CommonException;
import com.jiaozi.core.security.properties.JiaoZiJwtPropertie;
import com.jiaozi.core.security.properties.JiaoZiSecurityPropertie;
import com.jiaozi.core.security.pojo.LoginUserDetails;
import com.jiaozi.base.utils.bean.enums.REnum;
import com.jiaozi.system.enums.ConfigsEnum;
import com.jiaozi.system.service.SysConfigsService;
import com.jiaozi.base.utils.LogUtil;
import io.jsonwebtoken.*;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;


import java.io.IOException;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT过滤器
 */
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    /**
     * Redis工具类
     */
    @Autowired
    private RedisUtil redisUtil;
    @Resource
    private JiaoZiJwtPropertie jiaoziJwtPropertie;
    @Resource
    private JiaoZiSecurityPropertie jiaoziSecurityPropertie;
    @Autowired
    private SysConfigsService sysConfigsService;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
//        添加请求链路
        LogUtil.setTraceId();
        log.info(httpServletRequest.getRemoteAddr());
        try {

            if(antPathMatcher(httpServletRequest.getServletPath())){
                // 登录放行
                filterChain.doFilter(httpServletRequest, httpServletResponse);
            }else{
                // 获取Token
                String token = httpServletRequest.getHeader(jiaoziJwtPropertie.getTokenHerderKey());
                if (StrUtil.isEmpty(token)) {
                    exception( httpServletRequest,  httpServletResponse, new CommonException(REnum.LOGIN_TOKEN_ERROR, "异常请求"));
                }else{
                    token = token.substring(7);
                }

                // 校验Token是否有效（载荷无效和Token过期异常会传递给异常控制器并引发全局异常，然后统一处理）
                Claims claims = null;
                try {
                    claims = Jwts.parser().setSigningKey(jiaoziJwtPropertie.getSalt()).parseClaimsJws(token).getBody();
                } catch (Exception e) {
                    log.error("TOKEN失效,登录信息过期",e);
                    exception( httpServletRequest,  httpServletResponse, new CommonException(REnum.LOGIN_TOKEN_ERROR, "登录信息过期"));
                }
                // 获取用户id
                String userId = claims.get(jiaoziJwtPropertie.getUserHerderKey()).toString();
                String loginId = claims.get(jiaoziJwtPropertie.getLoginIdKey()).toString();

                // 获取缓存用户信息
                String userInfoCache = redisUtil.getString(RedisEnum.LOGIN_INFO, userId+":"+loginId);
                if (StrUtil.isEmpty(userInfoCache)) {
                    exception( httpServletRequest,  httpServletResponse, new CommonException(REnum.LOGIN_EXPIRE, "登录信息过期，请重新登录"));
                }else {
                    redisUtil.setStringExpire(RedisEnum.LOGIN_INFO , userId+":"+loginId,userInfoCache, Long.valueOf(sysConfigsService.getSysConfigs(ConfigsEnum.LOGIN_ACTIVE_EXPIRATION) ));
                }

                LoginUserDetails loginUser = JSONObject.parseObject(userInfoCache, LoginUserDetails.class);
                // 存入SecurityContextHolder
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null,loginUser.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                // 放行
                filterChain.doFilter(httpServletRequest, httpServletResponse);
            }

        } catch (Exception exception) {
            exception( httpServletRequest,  httpServletResponse, exception);
        }
    }


    /**
     * 路径匹配
     * @param servicePath
     * @return
     */
    public  boolean antPathMatcher(String servicePath) {

        AntPathMatcher antPathMatcher=new AntPathMatcher();
        for (String freeAccess : jiaoziSecurityPropertie.getFreeAccess()) {
            if(antPathMatcher.match(freeAccess,servicePath)){
                return true;
            }
        }

        for (String freeAccess : jiaoziSecurityPropertie.getStaticFiles()) {
            if(antPathMatcher.match(freeAccess,servicePath)){
                return true;
            }
        }
        return false;


    }


    /**
     * 异常重定向
     * @param httpServletRequest
     * @param httpServletResponse
     * @param commonException
     * @throws ServletException
     * @throws IOException
     */
    public void exception(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,Exception commonException) throws ServletException, IOException {
            httpServletRequest.setAttribute("exception", commonException);
            httpServletRequest.getRequestDispatcher("/exception").forward(httpServletRequest, httpServletResponse);
    }

}